#include "PerlinNoise.h"
#include <iostream>
#include <cmath>
#include <cfloat>
#include <vector>

PerlinNoise::PerlinNoise()
{
}

PerlinNoise::PerlinNoise(float per, float freq, float oct)
{
	persistence = per;
	frequency = freq;
	octaves = oct;
}
PerlinNoise::~PerlinNoise() {}

float PerlinNoise::perlNoise1D(float x, bool mult)
{
	  float total = 0;
 //     float p = .5;   //persistence
      float n = octaves;  //number of octaves-1

      for (int i=1;i<=n;i++)
	  {
		  octaveNo = i;
          float changeFreq = pow(frequency,i);
          float amplitude = pow(persistence,i);

          total = total + interpolatedNoise1D(x * frequency) * amplitude;
	  }
      if (!mult)
		return total;
	  else
		 return int((total+1)*127);

}

float PerlinNoise::perlNoise2D(float x,float y, bool mult)
{
	 float total = 0;
    // float p = .5;   //persistence
     float n = octaves;  //number of octaves-1
	
	//octaveNo = 1;
     for (int i=1;i<=n;i++)
	 {
		 octaveNo=i;
         float changeFreq = pow(frequency,i);
         float amplitude = pow(persistence,i);

		 /*std::cout << total;
		 std::cout << "\n";
		 std::cout << "\n";
		 std::cout << interpolatedNoise2D(x * frequency,y*frequency) * amplitude;
		 std::cout << "\n";
		 std::cout << "\n";*/

         total = total + interpolatedNoise2D(x * frequency,y*frequency) * amplitude;
     }

	 if(total<-1)
		 total=-1.0;
	 if(total>1)
		 total=1.0;
	 if (!mult)
		return total;
	 else
		 return int((total+1)*127);
		//return int((total+1)*127);
}

float PerlinNoise::rand1D(int x)
{	
	if (octaveNo==1)
	{	
		x = (x<<13) ^ x;
		return ( 1.0 - ( (x * (x * x * 15887 + 789961) + 1376310757) & 0x7FFFFFFF) / 1073741824.0);    
	}
	if(octaveNo==2)
	{
		x = (x<<13) ^ x;
		return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7FFFFFFF) / 1073741824.0);
	}
	
}

float PerlinNoise::rand2D(int x, int y)
{
	int n;
	n = x + y * 57;
	n = (n<<13) ^ n;
	if (octaveNo==1)
	{
	//return ( 1.0 - ( n * (n * n * 15731 + 789221) + 1376312589) / 1073741824.0)+3;    
		return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7FFFFFFF) / 1073741824.0); 
	}
	if (octaveNo==2)
	{
		return ( 1.0 - ( (n * (n * n * 15887 + 789961) + 1376310757) & 0x7FFFFFFF) / 1073741824.0);  
	}
		if (octaveNo==3)
	{
		return ( 1.0 - ( (n * (n * n * 15923 + 781961) + 1376319907) & 0x7FFFFFFF) / 1073741824.0);  
	}
		if (octaveNo==4)
	{
		return ( 1.0 - ( (n * (n * n * 15073 + 785923) + 1376313779) & 0x7FFFFFFF) / 1073741824.0);  
	}
		if (octaveNo==5)
	{
		return ( 1.0 - ( (n * (n * n * 15601 + 786829) + 1376310241) & 0x7FFFFFFF) / 1073741824.0);  
	}
		if (octaveNo==6)
	{
		return ( 1.0 - ( (n * (n * n * 15991 + 784867) + 1376318579) & 0x7FFFFFFF) / 1073741824.0);  
	}
		if (octaveNo==7)
	{
		return ( 1.0 - ( (n * (n * n * 15511 + 788419) + 1376315879) & 0x7FFFFFFF) / 1073741824.0);  
	}
		if (octaveNo==8)
	{
		return ( 1.0 - ( (n * (n * n * 15877 + 786419) + 1376310343) & 0x7FFFFFFF) / 1073741824.0);  
	}
	//return ( 1.0 - ( (x * (x * x* 15731 + 789221) + 1376312589) & 0x7FFFFFFF) / 1073741824.0); 
}

float PerlinNoise::cosineInterpolate(float a, float b, float x)
{
	float ft = x * 3.1415927;
	float f  = (1 - cos(ft)) * .5;

	return  a*(1-f) + b*f;

}


float PerlinNoise::linearInterpolate(float a, float b, float x)
{
	return  a*(1-x) + b*x;
}

float PerlinNoise::interpolatedNoise1D(float x)
{
	  int xInt    = int(x);
      float fracX = x - xInt;

      float v1 = smoothNoise1D(float(xInt));
      float v2 = smoothNoise1D(float(xInt + 1));

      return cosineInterpolate(v1 , v2 , fracX);

}

float PerlinNoise::interpolatedNoise2D(float x, float y)
{
	  int xInt   = int(x);
      float fracX = x - xInt;

      int yInt = int(y);
      float fracY = y - yInt;

      float v1 = smoothNoise2D(float(xInt),float(yInt));
      float v2 = smoothNoise2D(float(xInt + 1), float(yInt));
      float v3 = smoothNoise2D(float(xInt),float(yInt + 1));
      float v4 = smoothNoise2D(float(xInt + 1), float(yInt + 1));

      float i1 = cosineInterpolate(v1 , v2 , fracX);
      float i2 = cosineInterpolate(v3 , v4 , fracX);

      return cosineInterpolate(i1 , i2 , fracY);

}

float PerlinNoise::smoothNoise1D(float x)
{
	return rand1D(x)/2  +  rand1D(x-1)/4  +  rand1D(x+1)/4;

}

float PerlinNoise::smoothNoise2D(float x, float y)
{
	float corners = ( rand2D(x-1, y-1)+rand2D(x+1, y-1)+rand2D(x-1, y+1)+rand2D(x+1, y+1) ) / 16;
    float sides   = ( rand2D(x-1, y)  +rand2D(x+1, y)  +rand2D(x, y-1)  +rand2D(x, y+1) ) /  8;
    float center  =  rand2D(x, y) / 4;
    return corners + sides + center;

}


/*int main(void)
{


//	float x;
	PerlinNoise perl;
	perl = PerlinNoise(1.0,2.0,8);
	
	//perl.heightMap.resize(256);
	//for(int i=0;i<256;i++)
	//	perl.heightMap[i].resize(256);

	int heightMap [256][256];

//	x = PerlinNoise::PerlNoise1D(1);
	//cout << x;
	//for (int i=0;i<150;i++)
	//	std::cout << perl.perlNoise1D(i); "\n";
	for (int i=0;i<256;i++)
	{
		for (int j=0;j<256;j++)
		{
			/*std::cout << i;
			std::cout << " ";
			std::cout << j;
			std::cout << " ";
			std::cout << perl.perlNoise2D(i,j,true); 
		    std::cout << "\n";
			*/
			/*
			//perl.heightMap[i][j] = perl.perlNoise2D(i,j,true);
			heightMap[i][j] = perl.perlNoise2D(i,j,true);
		}
	}

  bool result = intarray2bmp::intarray2bmp( "foo2.bmp", &(heightMap[0][0]), 256, 256, 0, 255 );


}*/

/*int main(void)
{
	PerlinNoise perl;
	perl = PerlinNoise(1.0,2.0,8);

	perl.perlNoise2D(1,127,true);
}*/



